unit IFinder;

interface
uses SysUtils;

type
  TAttrType = (atAnyFile = faAnyFile, atReadOnly = faReadOnly,
                atHidden = faHidden, atSysFile=faSysFile,
                atVolumeID = faVolumeID, atDirectory = faDirectory,
                atArchive = faArchive);

  PResult = ^TResult;
  TResult = record
    Name: string;
    Type_: TAttrType;
    Time: TDateTime;
  end;


{$REGION 'TFinder = class'}
type
  PFinder = ^TFinder;
  TFinder = class
  private
    FSearchRec: TSearchRec;
    FResultCount: Integer;
//    function GetRN(const Index: Integer): string; inline;
    FResults: array of TResult;
    function GetResult(const Index: LongInt): TResult; inline;
  public
    constructor Create;
    destructor Destroy; override;
    function FindFirst(const Path: string; const Attr: TAttrType): Integer;
    function FindNext: Integer;
    procedure FindClose;
    procedure Find(const Path: string; const Attr: TAttrType = atAnyFile);
    procedure ClearResults;
    procedure Sort;

    property SearchRec: TSearchRec read FSearchRec write FSearchRec;
//    property ResultsNames[const Index: Integer]: string read GetRN; default;
    property ResultCount: Integer read FResultCount;
    property Result[const Index: LongInt]: TResult read GetResult;
  end;
{$ENDREGION}

implementation

type
  TCompareFunc = function (Item1, Item2: Pointer): Integer;

{$REGION 'TFinder'}
constructor TFinder.Create;
begin
  inherited;
end;

destructor TFinder.Destroy;
begin
  inherited;
end;

//function TFinder.GetRN(const Index: Integer): string;
//begin
//  Result := FResultsNames[Index];
//end;

function TFinder.FindFirst(const Path: string; const Attr: TAttrType): Integer;
begin
  Result := SysUtils.FindFirst(Path, Ord(Attr), FSearchRec);
end;

function TFinder.FindNext:Integer;
begin
  Result := SysUtils.FindNext(FSearchRec);
end;

procedure TFinder.FindClose;
begin
  SysUtils.FindClose(FSearchRec);
end;

procedure TFinder.Find(const Path: string; const Attr: TAttrType);
begin
  FindFirst(Path, Attr);
  FResultCount := 0;
  SetLength(FResults, FResultCount);
  repeat
    if Length(FSearchRec.Name) > 0 then begin
      Inc(FResultCount);
      SetLength(FResults, FResultCount);
      with FResults[FResultCount - 1] do begin
        Name := FSearchRec.Name;
        Time := FileDateToDateTime(FSearchRec.Time);
        case FSearchRec.Attr of
          faAnyFile: Type_ := atAnyFile;
          faReadOnly: Type_ := atReadOnly;
          faHidden: Type_ := atHidden;
          faSysFile: Type_ := atSysFile;
          faVolumeID: Type_ := atVolumeID;
          faDirectory: Type_ := atDirectory;
          faArchive: Type_ := atArchive;
        end;
      end;
    end;
  until FindNext <> 0;
  FindClose;
end;

procedure TFinder.ClearResults;
begin
  SetLength(FResults, 0);
  FResultCount := 0;
end;

procedure TFinder.Sort();
  function CompareFunc(Item1, Item2: PResult): LongInt;
    {$IFNDEF ICORE}
    function Sign(const x: Single): Integer;
    begin
      if x > 0 then
       Result := 1
      else
      if x < 0 then
       Result := -1
      else
       Result := 0;
    end;
    {$ENDIF}
  begin
    {$IFDEF ICORE}
      Result := TMath.Sign(Item1.Time - Item2.Time);
    {$ELSE}
      Result := Sign(Item1.Time - Item2.Time);
    {$ENDIF}
  end;
  procedure SortFragment(L, R: LongInt);
  var
    i, j: LongInt;
    p, t: PResult;
  begin
    repeat
      i := L;
      j := R;
      p := @FResults[(L + R) div 2];
      repeat
        while (CompareFunc(@FResults[i], p) < 0) do
         Inc(i);
        while (CompareFunc(@FResults[j], p) > 0) do
         Dec(j);
        if (i <= j) then
        begin
          t := @FResults[i];
          FResults[i] := FResults[j];
          FResults[j] := t^;
          Inc(i);
          Dec(j);
        end;
      until i > j;
      if (L < j) then
       SortFragment(L, j);
      L := i;
    until i >= R;
  end;
begin
  if (ResultCount > 1) then
   SortFragment(0, ResultCount - 1);
end;

function TFinder.GetResult(const Index: Integer): TResult;
begin
  if (Index >= 0) and (Index < FResultCount) then
   Result := FResults[Index];
end;

{$ENDREGION}

end.
